<?php

namespace App\Http\Utils\OAuth1;

/**
 *
 * @ignore
 *
 */
class OAuthServer {
	protected $timestamp_threshold = 300; // in seconds, five minutes
	protected $version = 1.0; // hi blaine
	protected $signature_methods = array ();
	protected $data_store;
	function __construct($data_store) {
		$this->data_store = $data_store;
	}
	public function add_signature_method($signature_method) {
		$this->signature_methods [$signature_method->get_name ()] = $signature_method;
	}
	
	// high level functions
	
	/**
	 * process a request_token request
	 * returns the request token on success
	 */
	public function fetch_request_token(&$request) {
		$this->get_version ( $request );
		
		$consumer = $this->get_consumer ( $request );
		
		// no token required for the initial token request
		$token = NULL;
		
		$this->check_signature ( $request, $consumer, $token );
		
		$new_token = $this->data_store->new_request_token ( $consumer );
		
		return $new_token;
	}
	
	/**
	 * process an access_token request
	 * returns the access token on success
	 */
	public function fetch_access_token(&$request) {
		$this->get_version ( $request );
		
		$consumer = $this->get_consumer ( $request );
		
		// requires authorized request token
		$token = $this->get_token ( $request, $consumer, "request" );
		
		$this->check_signature ( $request, $consumer, $token );
		
		$new_token = $this->data_store->new_access_token ( $token, $consumer );
		
		return $new_token;
	}
	
	/**
	 * verify an api call, checks all the parameters
	 */
	public function verify_request(&$request) {
		$this->get_version ( $request );
		$consumer = $this->get_consumer ( $request );
		$token = $this->get_token ( $request, $consumer, "access" );
		$this->check_signature ( $request, $consumer, $token );
		return array (
				$consumer,
				$token 
		);
	}
	
	// Internals from here
	/**
	 * version 1
	 */
	private function get_version(&$request) {
		$version = $request->get_parameter ( "oauth_version" );
		if (! $version) {
			$version = 1.0;
		}
		if ($version && $version != $this->version) {
			throw new OAuthNewException ( "OAuth version '$version' not supported" );
		}
		return $version;
	}
	
	/**
	 * figure out the signature with some defaults
	 */
	private function get_signature_method(&$request) {
		$signature_method = @$request->get_parameter ( "oauth_signature_method" );
		if (! $signature_method) {
			$signature_method = "PLAINTEXT";
		}
		if (! in_array ( $signature_method, array_keys ( $this->signature_methods ) )) {
			throw new OAuthNewException ( "Signature method '$signature_method' not supported " . "try one of the following: " . implode ( ", ", array_keys ( $this->signature_methods ) ) );
		}
		return $this->signature_methods [$signature_method];
	}
	
	/**
	 * try to find the consumer for the provided request's consumer key
	 */
	private function get_consumer(&$request) {
		$consumer_key = @$request->get_parameter ( "oauth_consumer_key" );
		if (! $consumer_key) {
			throw new OAuthNewException ( "Invalid consumer key" );
		}
		
		$consumer = $this->data_store->lookup_consumer ( $consumer_key );
		if (! $consumer) {
			throw new OAuthNewException ( "Invalid consumer" );
		}
		
		return $consumer;
	}
	
	/**
	 * try to find the token for the provided request's token key
	 */
	private function get_token(&$request, $consumer, $token_type = "access") {
		$token_field = @$request->get_parameter ( 'oauth_token' );
		$token = $this->data_store->lookup_token ( $consumer, $token_type, $token_field );
		if (! $token) {
			throw new OAuthNewException ( "Invalid $token_type token: $token_field" );
		}
		return $token;
	}
	
	/**
	 * all-in-one function to check the signature on a request
	 * should guess the signature method appropriately
	 */
	private function check_signature(&$request, $consumer, $token) {
		// this should probably be in a different method
		$timestamp = @$request->get_parameter ( 'oauth_timestamp' );
		$nonce = @$request->get_parameter ( 'oauth_nonce' );
		
		$this->check_timestamp ( $timestamp );
		$this->check_nonce ( $consumer, $token, $nonce, $timestamp );
		
		$signature_method = $this->get_signature_method ( $request );
		
		$signature = $request->get_parameter ( 'oauth_signature' );
		$valid_sig = $signature_method->check_signature ( $request, $consumer, $token, $signature );
		
		if (! $valid_sig) {
			throw new OAuthNewException ( "Invalid signature" );
		}
	}
	
	/**
	 * check that the timestamp is new enough
	 */
	private function check_timestamp($timestamp) {
		// verify that timestamp is recentish
		$now = time ();
		if ($now - $timestamp > $this->timestamp_threshold) {
			throw new OAuthNewException ( "Expired timestamp, yours $timestamp, ours $now" );
		}
	}
	
	/**
	 * check that the nonce is not repeated
	 */
	private function check_nonce($consumer, $token, $nonce, $timestamp) {
		// verify that the nonce is uniqueish
		$found = $this->data_store->lookup_nonce ( $consumer, $token, $nonce, $timestamp );
		if ($found) {
			throw new OAuthNewException ( "Nonce already used: $nonce" );
		}
	}
}